package corn

import (
	"gitee.com/yysf-xin/dproxy/pkg/app"
	"gitee.com/yysf-xin/dproxy/pkg/db"
	"gitee.com/yysf-xin/dproxy/pkg/model"
	"gitee.com/yysf-xin/dproxy/pkg/rest"
	"gitee.com/yysf-xin/dproxy/pkg/tools"
	"github.com/go-chi/chi/v5"
	"github.com/robfig/cron/v3"
	"gorm.io/gorm"
	"net/http"
	"sync"
)

type Control struct {
	db    *gorm.DB
	corn  *cron.Cron
	idMap map[uint]cron.EntryID
	mx    sync.Mutex
}

func NewCornControl(db *db.Client) *Control {
	c := &Control{
		db:    db.Db,
		corn:  cron.New(cron.WithSeconds()),
		idMap: make(map[uint]cron.EntryID),
	}
	c.doReload()
	c.corn.Start()
	return c
}

func (c *Control) InitRoute(router chi.Router) {
	router.Route("/{id}/", func(r chi.Router) {
		r.Post("/enable", app.JsonHttp(func() interface{} {
			return &model.Enable{}
		}, c.handlerEnable))
		r.Get("/run", c.runJob)
	})

	router.Get("/list", c.listAll)
	router.Get("/reload", c.reload)
	router.Post("/enable", app.JsonHttp(func() interface{} {
		return &model.Enable{}
	}, c.handlerStatus))

}
func (c *Control) handlerStatus(writer http.ResponseWriter, request *http.Request, i interface{}) (interface{}, error) {
	if i.(*model.Enable).Enabled {
		c.corn.Run()
	} else {
		c.corn.Stop()
	}
	return nil, nil
}
func (c *Control) handlerEnable(_ http.ResponseWriter, r *http.Request, i interface{}) (rs interface{}, err error) {
	id, err := rest.PathParamId(r)
	if err != nil {
		return
	}
	var (
		tId cron.EntryID
		ok  bool
	)
	c.mx.Lock()
	tId, ok = c.idMap[id]
	c.mx.Unlock()

	var p model.CornConfig
	if i.(*model.Enable).Enabled {
		if ok {
			return
		}
		err = c.db.First(&p, id).Error
		if err != nil {
			return
		}
		tId, err = c.addJob(&p)
		c.mx.Lock()
		c.idMap[id] = tId
		c.mx.Unlock()
	} else if ok {
		c.mx.Lock()
		delete(c.idMap, id)
		c.mx.Unlock()
		c.corn.Remove(tId)
	}
	err = c.db.Model(&p).Where("id = ?", id).Update("enabled", i.(*model.Enable).Enabled).Error
	return
}

func (c *Control) listAll(w http.ResponseWriter, r *http.Request) {
	app.RenderJSON(w, c.corn.Entries(), nil)
}

func (c *Control) reload(writer http.ResponseWriter, request *http.Request) {
	c.doReload()
}

func (c *Control) doReload() {
	for _, v := range c.corn.Entries() {
		c.corn.Remove(v.ID)
	}
	var list []*model.CornConfig
	c.db.Find(&list, "enabled = ?", true)

	c.mx.Lock()
	defer c.mx.Unlock()
	for i := range list {
		t := list[i]
		tId, err := c.addJob(t)
		if err != nil {
			tools.Log.Println("start task", t.Id, t.Name, err)
		} else {
			c.idMap[t.Id] = tId
		}
	}
}

func (c *Control) addJob(p *model.CornConfig) (tId cron.EntryID, err error) {
	j, err := c.createJob(p)
	if err == nil {
		tId, err = c.corn.AddJob(p.Corn, j)
	}
	return
}

func (c *Control) createJob(p *model.CornConfig) (j cron.Job, err error) {
	var cr JobCreator
	cr, err = GetJob(p.Type)
	if err != nil {
		return
	}
	j, err = cr(c, p)
	return
}

func (c *Control) runJob(w http.ResponseWriter, r *http.Request) {
	id, err := rest.PathParamId(r)
	var p model.CornConfig
	if err == nil {
		err = c.db.First(&p, id).Error
	}
	if err == nil {
		var j cron.Job
		j, err = c.createJob(&p)
		if err == nil {
			go j.Run()
		}
	}
	app.RenderJSON(w, p.Id, err)

}
